#ifndef _NETWORK_SPIDEVMANAGEMENT_INTERFACE_SPIBUFFERMANAGER_
#define _NETWORK_SPIDEVMANAGEMENT_INTERFACE_SPIBUFFERMANAGER_

#include <memory>
#include <vector>
#include <map>
#include <shared_mutex>

static constexpr size_t CF_DATA_SIZE = 128;
static constexpr size_t DF_DATA_SIZE = 8*1024;

enum class Buffer_Id : uint8_t
{
    SOC_OUT_CF = 0x01,
    SOC_IN_CF = 0x02,
    SOC_OUT_DF = 0x03,
    SOC_IN_DF = 0x04,
};

struct Buffer_User_Info
{
    uint16_t start_position_;
    size_t   length_;
};

using Unique_Buffer_Identify = std::string;
using Buffer_User_Group = std::map<Unique_Buffer_Identify, Buffer_User_Info>;

class Base_Data_Buffer
{
public:
    Base_Data_Buffer(const size_t& data_size);
    virtual ~Base_Data_Buffer()  = default;

    virtual bool vaild_and_set(const std::string& unique_id, const uint16_t& start_position, const size_t& length);

private:
    Buffer_User_Group its_user_group;
    std::vector<bool> its_using_flag;
};

class Soc_Out_Data_Buffer : public Base_Data_Buffer
{
public:
    Soc_Out_Data_Buffer(const size_t& data_size);
    ~Soc_Out_Data_Buffer() = default;

    Soc_Out_Data_Buffer(const Soc_Out_Data_Buffer&) = delete;
    Soc_Out_Data_Buffer& operator=(const Soc_Out_Data_Buffer&) = delete;
    
    template<class T>
    bool write_buffer(const T& payload, const size_t& start_address);
    bool copy_full_buffer(uint8_t* output, const size_t& size);

private:
    std::vector<uint8_t> its_buffer;
    std::shared_mutex    its_shared_mutex;  //Write monopoly
};

template<class T>
bool Soc_Out_Data_Buffer::write_buffer(const T& payload, const size_t& start_address)
{
    std::shared_lock<std::shared_mutex> shared_write_lock(its_shared_mutex);
    size_t length = sizeof(T);
    if(length + start_address <= its_buffer.size())
    {
        const uint8_t* pdata = reinterpret_cast<const uint8_t*>(&payload);
        std::copy(pdata, pdata+length, its_buffer.data()+start_address);
        return true;
    }
    else
    {
        return false;
    }
}

class Soc_In_Data_Buffer: public Base_Data_Buffer
{
public:
    Soc_In_Data_Buffer(const size_t& data_size);
    ~Soc_In_Data_Buffer() = default;

    Soc_In_Data_Buffer(const Soc_In_Data_Buffer&) = delete;
    Soc_In_Data_Buffer& operator=(const Soc_In_Data_Buffer&) = delete;
    
    template<class T>
    bool read_buffer(const size_t& start_address, T& payload);
    bool copy_full_buffer(const uint8_t* const input, const size_t& size);

    bool wait_for_update();
    void notify_update();

private:
    std::vector<uint8_t> its_buffer;
    std::shared_mutex    its_shared_mutex;  //Write monopoly
    std::mutex           its_update_mutex;
    std::condition_variable its_update_cond;
};

template<class T>
bool Soc_In_Data_Buffer::read_buffer(const size_t& start_address, T& payload)
{
    std::shared_lock<std::shared_mutex> shared_write_lock(its_shared_mutex);
    size_t length = sizeof(T);
    if(length + start_address <= its_buffer.size())
    {
        uint8_t* pdata = reinterpret_cast<uint8_t*>(&payload);
        std::fill_n(pdata, length, 0);
        std::copy(its_buffer.data() + start_address, its_buffer.data() + start_address + sizeof(T), pdata);
        return true;
    }
    else
    {
        return false;
    }
}


#endif


